// utilregEx.cpp: implementation of the CUTILregEx class.
//
//////////////////////////////////////////////////////////////////////

#include <windows.h>
#include <tchar.h>
#include <string.h>
#include "utilregEx.h"

#define SCRAMBLE_PREAMBLE_LENGTH	4
#define SCRAMBLE_POSTAMBLE_LENGTH	4
#define STRING_LENGTH			MAX_PATH

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CUTILregEx::CUTILregEx(const HKEY hKeyRoot, LPCTSTR lpKey, REGSAM samDesired)
{
	m_hLmKey = NULL;
	m_bKeyOpen = FALSE;
	m_hKeyRoot = hKeyRoot;
	m_samOpenSecurity = samDesired;

	//no create if we're just reading
	m_bAllowCreate = ((samDesired == KEY_QUERY_VALUE) || (samDesired == KEY_READ)) ? FALSE : TRUE;

	ZeroMemory(m_lpKey, MAX_PATH*sizeof(m_lpKey[0]));
	lstrcpy(m_lpKey, lpKey);
	// open/create key
	if (RegOpenKeyEx(m_hKeyRoot, m_lpKey, 0, samDesired, &m_hKey)==ERROR_SUCCESS)
	{
		m_bKeyOpen = TRUE;
	}
	else if (CreateKey(m_hKeyRoot, m_lpKey, &m_hKey)==ERROR_SUCCESS)
	{
		m_bKeyOpen = TRUE;
	}
}

CUTILregEx::CUTILregEx(BOOL bTryBaseKey, REGSAM samDesired)
{
	m_hLmKey = NULL;
	//UTILos l_os = UTIL_GetOS();
	m_bKeyOpen = FALSE;
	m_bAllowCreate = TRUE;
	m_samOpenSecurity = samDesired;
	ZeroMemory(m_lpKey, MAX_PATH*sizeof(m_lpKey[0]));

	if ( bTryBaseKey )
	{
		TCHAR key[MAX_PATH];
		ZeroMemory(key, MAX_PATH);

		// read key
		GetRegKeyBase(key, MAX_PATH);
		if(_tcslen(key) == 0)
			return;
		Init(key);
	}
}

CUTILregEx::CUTILregEx(LPCTSTR lpKey, REGSAM samDesired)
{
	m_hLmKey = NULL;
	m_bKeyOpen = FALSE;
	m_samOpenSecurity = samDesired;
	//no create if we're just reading
	m_bAllowCreate = ((samDesired == KEY_QUERY_VALUE) || (samDesired == KEY_READ)) ? FALSE : TRUE;
	ZeroMemory(m_lpKey, MAX_PATH*sizeof(m_lpKey[0]));
	Init(lpKey);
}

HRESULT CUTILregEx::Init(LPCTSTR lpKey)
{
	HKEY hKeyLM;

	m_hKeyRoot = HKEY_CURRENT_USER;
	lstrcpy(m_lpKey, lpKey);

	if (RegOpenKeyEx(m_hKeyRoot, m_lpKey, 0, m_samOpenSecurity, &m_hKey)==ERROR_SUCCESS)
	{
		// key exists
		m_bKeyOpen = TRUE;
	}
	else if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, m_lpKey, 0, KEY_READ, &hKeyLM)==ERROR_SUCCESS)
	{
		// create and copy keys and values
		if (CreateKey(m_hKeyRoot, m_lpKey, &m_hKey)==ERROR_SUCCESS)
		{
			CopyValues(m_hKey, hKeyLM);
			m_bKeyOpen = TRUE;
		}
		RegCloseKey(hKeyLM);
	}
	else if (CreateKey(m_hKeyRoot, m_lpKey, &m_hKey)==ERROR_SUCCESS)
	{
		// create key
		m_bKeyOpen = TRUE;
	}

	if(m_hKeyRoot == HKEY_CURRENT_USER)
	{ // create the HKLM key so that data that is not in HKCU can still be read
		if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, m_lpKey, 0, KEY_QUERY_VALUE, &m_hLmKey) == ERROR_SUCCESS)
			m_bKeyOpen = TRUE;
	}
	return S_OK;
}

CUTILregEx::~CUTILregEx()
{
	if (m_bKeyOpen)
	{
		RegCloseKey(m_hKey);
	}

	if(m_hLmKey)
		RegCloseKey(m_hLmKey);
}

CUTILregEx& CUTILregEx::operator=(LPCTSTR pszName)
{
	TCHAR key[MAX_PATH];

	// close any opened registry
	if (m_bKeyOpen)
		RegCloseKey(m_hKey);

	if(m_hLmKey)
	{
		RegCloseKey(m_hLmKey);
		m_hLmKey = NULL;
	}

	m_bKeyOpen = FALSE;
	// Preserve the SAM and KEY CREATION settings as before.
	//m_samOpenSecurity = (KEY_READ|KEY_WRITE);
	//m_bAllowCreate = TRUE;
	ZeroMemory(m_lpKey, MAX_PATH*sizeof(m_lpKey[0]));

	if ( !pszName || pszName[0]=='\0' )
	{
		ZeroMemory(key, MAX_PATH);
		// read key
		GetRegKeyBase(key, MAX_PATH);
		if(_tcslen(key) == 0)
			return *this;
	}
	else
	{
		_tcscpy(key, pszName);
	}

	Init(key);

	return *this;
}

//////////////////////////////////////////////////////////////////////
// These methods are used to allow opening the registry without knowlege 
// of the underlying applicaiton
//////////////////////////////////////////////////////////////////////
#define REG_BASE_FMT_STR _T("__@@82763207_AA34_44e6_A91B_19576293E78A_")

void CUTILregEx::SetRegKeyBase(LPCTSTR baseKey)
{
	SetEnvironmentVariable(REG_BASE_FMT_STR, baseKey);
}

void CUTILregEx::GetRegKeyBase(LPTSTR key, int bufLen)
{
	TCHAR valBuf[MAX_PATH];
	memset(key, 0, bufLen * sizeof(TCHAR));
	if(GetEnvironmentVariable(REG_BASE_FMT_STR, valBuf, MAX_PATH) > 0)
		_tcsncpy(key, valBuf, bufLen);
}


//////////////////////////////////////////////////////////////////////
// This method creates key
//////////////////////////////////////////////////////////////////////

HRESULT CUTILregEx::CreateKey(
							  HKEY hKey,					// handle to open key
							  LPCTSTR lpSubKey,			// subkey name
							  PHKEY phkResult,			// key handle 
							  DWORD dwOptions,			// special options
							  REGSAM samDesired			// desired security access
							  )

{
	DWORD dwDisposition;
	if (!m_bAllowCreate)
		return E_FAIL;

	return RegCreateKeyEx(hKey, lpSubKey, 0, _T(""), 
		dwOptions, samDesired, NULL, phkResult, &dwDisposition);
}


//////////////////////////////////////////////////////////////////////
// This method walks through the subkeys of a main key using the
//	passed in index for the API call... To star the search for subkeys
//	simply reset the value to zero.  FAILS when there are no more 
//	objects to enumerate... 
// Note that this function will skip over VALUES and only return other
//	keys within the scope of the currently opened key...
// 01/15/03
//////////////////////////////////////////////////////////////////////

HRESULT CUTILregEx::EnumerateSubKeys(DWORD *pdwLastIdx, LPTSTR lpSubKeyName, DWORD dwNameMaxLen)
{
	DWORD dwLocalNameLen;
	LONG nRet = ERROR_SUCCESS;
	HRESULT hr = E_FAIL;
	FILETIME changeTime;

	if (!m_bKeyOpen)
		return E_HANDLE;

	// copy values
	while ( (nRet==ERROR_SUCCESS) && FAILED(hr) )
	{
		dwLocalNameLen = dwNameMaxLen;
		nRet = RegEnumKeyEx(m_hKey, (*pdwLastIdx), 
			lpSubKeyName, &dwLocalNameLen, NULL,
			NULL, NULL, &changeTime);
		switch (nRet)
		{
		case ERROR_SUCCESS:				//found another key
			(*pdwLastIdx)++;
			hr = S_OK;
			break;
		case ERROR_MORE_DATA:			//string too short
			hr = E_POINTER;
			break;
		case ERROR_NO_MORE_ITEMS:		//no more items found...
			hr = E_FAIL;
			break;
		}
	}
	return hr;
}

//////////////////////////////////////////////////////////////////////
// This method copies values and keys recursively 
//////////////////////////////////////////////////////////////////////

HRESULT CUTILregEx::CopyValues(HKEY hKeyDst, HKEY hKeySrc, CopyOptions Options)
{
	TCHAR lpName[MAX_PATH], lpData[MAX_PATH];
	DWORD cName, dwType, cbData;
	HKEY hKeyDst0, hKeySrc0;
	DWORD dwIndex;
	LONG nRet;

	// copy values
	dwIndex = 0;
	nRet = ERROR_SUCCESS;
	while(nRet==ERROR_SUCCESS)
	{
		cName = MAX_PATH;
		cbData = MAX_PATH*sizeof(lpData[0]);
		nRet = RegEnumValue(hKeySrc, dwIndex, 
			lpName, &cName, NULL, &dwType,
			(LPBYTE)lpData, &cbData);
		if (nRet==ERROR_SUCCESS)
		{
			RegSetValueEx(hKeyDst, lpName, 0, dwType, 
				(LPBYTE)lpData, cbData);
		}
		dwIndex++;
	}
	if (nRet!=ERROR_NO_MORE_ITEMS)
		return E_FAIL;

	// copy subkeys recursively
	if (Options==COPY_RECURSIVELY)
	{
		dwIndex = 0;
		nRet = ERROR_SUCCESS;
		while(nRet==ERROR_SUCCESS)
		{
			FILETIME ftLastWriteTime;
			cName = MAX_PATH;
			nRet = RegEnumKeyEx(hKeySrc, dwIndex,
				lpName, &cName, NULL, NULL, NULL, &ftLastWriteTime);
			if (nRet==ERROR_SUCCESS)
			{
				if (RegOpenKeyEx(hKeySrc, lpName, 0, KEY_READ, &hKeySrc0)!=ERROR_SUCCESS)
				{
					continue;
				}
				if (CreateKey(hKeyDst, lpName, &hKeyDst0)!=ERROR_SUCCESS)
				{
					RegCloseKey(hKeySrc0);
					continue;
				}
				CopyValues(hKeyDst0, hKeySrc0);
				RegCloseKey(hKeySrc0);
				RegCloseKey(hKeyDst0);
			}
			dwIndex++;
		}
		if (nRet!=ERROR_NO_MORE_ITEMS)
			return E_FAIL;
	}
	return ERROR_SUCCESS;
}

//////////////////////////////////////////////////////////////////////
// These methods set and get values 
//////////////////////////////////////////////////////////////////////

HRESULT CUTILregEx::SetDW(LPCTSTR lpValueName, DWORD dwValue, BOOL bSetOnlyWhenExisted)
{
	if (!m_bKeyOpen)
		return E_FAIL;
	if(bSetOnlyWhenExisted)
	{
		HRESULT hr;
		DWORD dwType, dwData, dwLen;
		hr = RegQueryValueEx(m_hLmKey ? m_hLmKey : m_hKey, lpValueName, NULL, &dwType, (LPBYTE)&dwData, &dwLen);
		if(hr != ERROR_SUCCESS)
			return hr;
	}
	return RegSetValueEx(m_hKey, lpValueName, 0L, REG_DWORD, (LPBYTE)&dwValue, sizeof(DWORD));
}

HRESULT CUTILregEx::GetDW(LPCTSTR lpValueName, LPDWORD lpdwValue, DWORD dwDefault)
{
	HRESULT hr = E_FAIL;
	DWORD dwType, dwLen = sizeof(DWORD);
	if (m_bKeyOpen)
	{
		hr = RegQueryValueEx(m_hKey, lpValueName, 0L, &dwType, (LPBYTE)lpdwValue, &dwLen);

		if((hr != ERROR_SUCCESS) && (m_hKeyRoot == HKEY_CURRENT_USER) && (m_hLmKey != NULL))
		{ // try HKLM
			hr = RegQueryValueEx(m_hLmKey, lpValueName, 0L, &dwType, (LPBYTE)lpdwValue, &dwLen);
		}
		if (dwType!=REG_DWORD || dwLen!=sizeof(DWORD) || hr!=ERROR_SUCCESS)
			hr = E_FAIL;
	}
	if (hr != ERROR_SUCCESS)
		*lpdwValue = dwDefault;
	return hr;
}

HRESULT CUTILregEx::SetSZ(LPCTSTR lpValueName, LPCTSTR tszValue)
{
	if (!m_bKeyOpen)
		return E_FAIL;
	return RegSetValueEx(m_hKey, lpValueName, 0L, REG_SZ, (LPBYTE)tszValue, _tcslen(tszValue)*sizeof(TCHAR));
}

HRESULT CUTILregEx::GetSZ(LPCTSTR lpValueName, LPCTSTR tszValue, LPDWORD lpdwLen)
{
	HRESULT hr = E_FAIL;
	DWORD dwType;
	if (m_bKeyOpen)
	{
		hr = RegQueryValueEx(m_hKey, lpValueName, 0L, &dwType, (LPBYTE)tszValue, lpdwLen);
		if(hr != ERROR_SUCCESS && m_hKeyRoot == HKEY_CURRENT_USER)
		{ // try HKLM
			hr = RegQueryValueEx(m_hLmKey, lpValueName, 0L, &dwType, (LPBYTE)tszValue, lpdwLen);
		}
		if (dwType !=REG_SZ || hr!=ERROR_SUCCESS)
			hr = E_FAIL;
	}
	return hr;
}

//////////////////////////////////////////////////////////////////////
// Public: set values 
//////////////////////////////////////////////////////////////////////
HRESULT CUTILregEx::SetValue(LPCTSTR lpValueName, long nValue, BOOL bSetOnlyWhenExisted)
{
	return SetDW(lpValueName, (DWORD)nValue, bSetOnlyWhenExisted);
}
HRESULT CUTILregEx::SetValue(LPCTSTR lpValueName, int nValue, BOOL bSetOnlyWhenExisted)
{
	return SetDW(lpValueName, (DWORD)nValue, bSetOnlyWhenExisted);
}
HRESULT CUTILregEx::SetValue(LPCTSTR lpValueName, ULONG nValue, BOOL bSetOnlyWhenExisted)
{
	return SetDW(lpValueName, nValue, bSetOnlyWhenExisted);
}
HRESULT CUTILregEx::SetValue(LPCTSTR lpValueName, UINT nValue, BOOL bSetOnlyWhenExisted)
{
	return SetDW(lpValueName, nValue, bSetOnlyWhenExisted);
}
HRESULT CUTILregEx::SetValue(LPCTSTR lpValueName, char* lpValue)
{
	return SetSZ(lpValueName, (LPCTSTR)lpValue);
}
HRESULT CUTILregEx::SetValue(LPCTSTR lpValueName, UCHAR* lpValue)
{
	return SetSZ(lpValueName, (LPCTSTR)lpValue);
}
HRESULT CUTILregEx::SetValue(LPCTSTR lpValueName, LPCTSTR lpValue, BOOL bScramble)
{
	if(bScramble)
	{
		TCHAR szTarget[STRING_LENGTH];
		memset(&szTarget, 0, sizeof(TCHAR)*STRING_LENGTH);
		Scramble(lpValue, (LPTSTR)szTarget);
		return SetSZ(lpValueName, (LPCTSTR)szTarget);
	}
	return SetSZ(lpValueName, (LPCTSTR)lpValue);
}
HRESULT CUTILregEx::SetBinValue(LPCTSTR lpValueName, LPCTSTR lpValue, BOOL bScramble)
{
	DWORD dwLen = _tcslen(lpValue);
	if(bScramble)
	{
		TCHAR szTarget[STRING_LENGTH];
		memset(&szTarget, 0, sizeof(TCHAR)*STRING_LENGTH);
		Scramble(lpValue, (LPTSTR)szTarget);
		dwLen = _tcslen((LPTSTR)szTarget);
		return SetBinValue(lpValueName, (BYTE*)szTarget, dwLen);
	}
	return SetBinValue(lpValueName, (BYTE*)lpValue, dwLen);
}
HRESULT CUTILregEx::SetValue(LPCTSTR lpValueName, LONGLONG llValue)
{
	TCHAR lpszConverted[MAX_PATH];
	_i64tot(llValue, lpszConverted, 10);
	return SetSZ(lpValueName, lpszConverted);
}

//////////////////////////////////////////////////////////////////////
// Public: get values with default input
//////////////////////////////////////////////////////////////////////
HRESULT CUTILregEx::GetValue(LPCTSTR lpValueName, long* lpValue, long nDefault)
{
	return GetDW(lpValueName, (LPDWORD)lpValue, (DWORD)nDefault);
}
HRESULT CUTILregEx::GetValue(LPCTSTR lpValueName, int* lpValue, int nDefault)
{
	return GetDW(lpValueName, (LPDWORD)lpValue, (DWORD)nDefault);
}
HRESULT CUTILregEx::GetValue(LPCTSTR lpValueName, ULONG* lpValue, ULONG nDefault)
{
	return GetDW(lpValueName, (LPDWORD)lpValue, nDefault); 
}
HRESULT CUTILregEx::GetValue(LPCTSTR lpValueName, UINT* lpValue, UINT nDefault)
{
	return GetDW(lpValueName, (LPDWORD)lpValue, nDefault); 
}
HRESULT CUTILregEx::GetValue(LPCTSTR lpValueName, LONGLONG* pllValue, LONGLONG llDefault)
{
	TCHAR lpszConverted[MAX_PATH];
	TCHAR lpszReadVal[MAX_PATH];
	HRESULT hrReadResult;
	DWORD dwMaxLen = MAX_PATH;

	_i64tot(llDefault, lpszConverted, 10);
	hrReadResult = GetSZ(lpValueName, (LPCTSTR)lpszReadVal, &dwMaxLen);

	if (SUCCEEDED(hrReadResult))
	{
		//convert it back to a longlong
		(*pllValue) = _ttoi (lpszReadVal);
		return hrReadResult;
	}
	else
	{
		//copy default longlong
		(*pllValue) = llDefault;
		return E_FAIL;
	}
}

//////////////////////////////////////////////////////////////////////
// Public: get values without default input
//////////////////////////////////////////////////////////////////////
HRESULT CUTILregEx::GetValue(LPCTSTR lpValueName, long* lpValue)
{
	return GetDW(lpValueName, (LPDWORD)lpValue, *((DWORD*)lpValue));
}
HRESULT CUTILregEx::GetValue(LPCTSTR lpValueName, int* lpValue)
{
	return GetDW(lpValueName, (LPDWORD)lpValue, *((DWORD*)lpValue));
}
HRESULT CUTILregEx::GetValue(LPCTSTR lpValueName, ULONG* lpValue)
{
	return GetDW(lpValueName, (LPDWORD)lpValue, (*lpValue)); 
}
HRESULT CUTILregEx::GetValue(LPCTSTR lpValueName, UINT* lpValue)
{
	return GetDW(lpValueName, (LPDWORD)lpValue, (*lpValue)); 
}
HRESULT CUTILregEx::GetValue(LPCTSTR lpValueName, LONGLONG* pllValue)
{
	TCHAR lpszReadVal[MAX_PATH];
	HRESULT hrReadResult;
	DWORD dwMaxLen = MAX_PATH;

	hrReadResult = GetSZ(lpValueName, (LPCTSTR)lpszReadVal, &dwMaxLen);

	if (SUCCEEDED(hrReadResult))
		//convert it back to a longlong
		(*pllValue) = _ttoi (lpszReadVal);

	return hrReadResult;
}

//////////////////////////////////////////////////////////////////////
// Public: get string values with length input
//////////////////////////////////////////////////////////////////////
HRESULT CUTILregEx::GetValue(LPCTSTR lpValueName, char* lpValue, LPDWORD lpdwLen)
{
	return GetSZ(lpValueName, (LPCTSTR)lpValue, lpdwLen);
}
HRESULT CUTILregEx::GetValue(LPCTSTR lpValueName, UCHAR* lpValue, LPDWORD lpdwLen)
{
	return GetSZ(lpValueName, (LPCTSTR)lpValue, lpdwLen);
}
HRESULT CUTILregEx::GetValue(LPCTSTR lpValueName, LPCTSTR lpValue, LPDWORD lpdwLen, BOOL bDescramble)
{
	HRESULT hr;
	hr = GetSZ(lpValueName, (LPCTSTR)lpValue, lpdwLen);
	if(bDescramble && SUCCEEDED(hr)) 
	{
		Descramble((LPTSTR)lpValue, *lpdwLen);
	}
	return hr;
}
HRESULT CUTILregEx::GetBinValue(LPCTSTR lpValueName, LPCTSTR lpValue, LPDWORD lpdwLen, BOOL bDescramble)
{
	HRESULT hr;
	hr = GetBinValue(lpValueName, (BYTE*)lpValue, lpdwLen);
	if((bDescramble && SUCCEEDED(hr)) || (bDescramble && *lpValue!='\0')) // allow REG_SZ to Descrambel when lpValue not NULL
	{
		Descramble((LPTSTR)lpValue, *lpdwLen);
	}
	return hr;
}
//////////////////////////////////////////////////////////////////////
// Public: get binary values with length input
//////////////////////////////////////////////////////////////////////
HRESULT CUTILregEx::GetBinValue(LPCTSTR lpValueName, LPBYTE lpValue, LPDWORD lpdwLen)
{
	HRESULT hr;
	DWORD dwType;
	if (!m_bKeyOpen)
		return E_FAIL;
	hr = RegQueryValueEx(m_hKey, lpValueName, 0L, &dwType, (LPBYTE)lpValue, lpdwLen);
	if(hr != ERROR_SUCCESS && m_hKeyRoot == HKEY_CURRENT_USER)
	{ // try HKLM
		hr = RegQueryValueEx(m_hLmKey, lpValueName, 0L, &dwType, (LPBYTE)lpValue, lpdwLen);
	}

	if (dwType !=REG_BINARY || hr!=ERROR_SUCCESS)
		hr = E_FAIL;
	return hr;
}

//////////////////////////////////////////////////////////////////////
// Public: set binary values with length input
//////////////////////////////////////////////////////////////////////
HRESULT CUTILregEx::SetBinValue(LPCTSTR lpValueName, LPBYTE lpValue, DWORD dwLen)
{
	if (!m_bKeyOpen)
		return E_FAIL;
	return RegSetValueEx(m_hKey, lpValueName, 0L, REG_BINARY, lpValue, dwLen);
}

//////////////////////////////////////////////////////////////////////
// Public: Delete key
//////////////////////////////////////////////////////////////////////

HRESULT	CUTILregEx::DeleteValue(LPCTSTR lpValueName)
{
	return RegDeleteValue(m_hKey, lpValueName) == ERROR_SUCCESS ? S_OK : E_FAIL;
}

//////////////////////////////////////////////////////////////////////
// Public: Delete Sub key
//////////////////////////////////////////////////////////////////////

HRESULT	CUTILregEx::DeleteSubKey(LPCTSTR lpSubKey, BOOL bRecursive)
{
	//HRESULT hr = E_FAIL;
	LONG nRet = ERROR_SUCCESS;

	if (!m_bKeyOpen)
		return E_HANDLE;

	if (bRecursive)
	{
		HKEY hDeleteTarget;
		nRet = RegOpenKey( m_hKey, lpSubKey, &hDeleteTarget ); 
		if ( ERROR_SUCCESS == nRet )
		{
			DeleteKeyRecursive(hDeleteTarget);
			RegCloseKey(hDeleteTarget);
		}
		else
		{
			return E_FAIL;
		}
	}
	return RegDeleteKey(m_hKey, lpSubKey) == ERROR_SUCCESS ? S_OK : E_FAIL;
}

//
// Recursively delete all keys and values from this registry key...
//	private access to this utility class only...
//
void CUTILregEx::DeleteKeyRecursive(HKEY hKey)
{
	int		nRet;
	TCHAR	szValueName[MAX_PATH];
	DWORD	dwValueMaxSize;
	DWORD	keyType;
	HKEY	hSubKey;

	do
	{
		dwValueMaxSize = MAX_PATH; 

		// Enumerate data values at current key 
		nRet = RegEnumValue( hKey, 0, szValueName, 
			&dwValueMaxSize, NULL, &keyType, NULL, NULL );

		// Delete old value 
		if ( ERROR_SUCCESS == nRet )
			RegDeleteValue( hKey, szValueName );
	}
	while ( ERROR_SUCCESS == nRet ); // Loop until all values found 

	// Start over, looking for keys now instead of values 
	do
	{
		nRet = RegEnumKey( hKey, 0, szValueName, MAX_PATH );

		// Was a valid key found? 
		if ( ERROR_SUCCESS == nRet )
		{
			// Open the key if found 
			nRet = RegOpenKey( hKey, szValueName, &hSubKey ); 
			if ( ERROR_SUCCESS == nRet )
			{
				// If that worked, recurse back here 
				DeleteKeyRecursive( hSubKey );
				RegCloseKey( hSubKey );
				RegDeleteKey( hKey, szValueName );
			}
		}
	}
	while ( ERROR_SUCCESS == nRet ); // Loop until key enum fails 
}


//////////////////////////////////////////////////////////////////////
// Private: Descramble value
//////////////////////////////////////////////////////////////////////
void CUTILregEx::Descramble(LPTSTR szSource, DWORD dwSize)
{
	INT Index=0;
	TCHAR sTarget[STRING_LENGTH];
	memset(sTarget, 0, sizeof(TCHAR)*STRING_LENGTH);
	szSource[dwSize] = '\0';
	for(INT i = SCRAMBLE_PREAMBLE_LENGTH; i < (INT )(_tcslen(szSource) - SCRAMBLE_POSTAMBLE_LENGTH); i+=2)
	{
		if ((i-SCRAMBLE_PREAMBLE_LENGTH)&2) 
			sTarget[Index++] = (szSource[i+1] - 20);
		else 
			sTarget[Index++] = (szSource[i] + 10);
	}
	_tcsncpy(szSource, (LPCTSTR)sTarget, dwSize);
}

//////////////////////////////////////////////////////////////////////
// Private: Scramble value
//////////////////////////////////////////////////////////////////////
void CUTILregEx::Scramble(LPCTSTR szSource, LPTSTR sTarget)
{
	UINT i;
	TCHAR szScramble[STRING_LENGTH];
	int Index = 0;
	ZeroMemory(szScramble, sizeof(TCHAR)*STRING_LENGTH);

	srand(timeGetTime());
	for(i = 0; i < SCRAMBLE_PREAMBLE_LENGTH; i++, Index++)
		szScramble[Index] = GetRandom();

	for(i = 0; i < _tcslen(szSource); i++, Index++)
	{
		if (i&1) 
		{	
			szScramble[Index++] = GetScrambledCode(i);
			szScramble[Index] = (szSource[i] + 20);
		}	
		else 
		{			
			szScramble[Index++] = (szSource[i] - 10);
			szScramble[Index] = GetScrambledCode(i);
		}
	}
	for(i = 0; i < SCRAMBLE_POSTAMBLE_LENGTH; i++, Index++)		
		szScramble[Index] = GetRandom();	
	_tcscpy(sTarget, szScramble);
}

//////////////////////////////////////////////////////////////////////
// Private: Get Scramble Code
//////////////////////////////////////////////////////////////////////
TCHAR CUTILregEx::GetScrambledCode(INT nOrder)
{
	TCHAR szCodeTable0[] = _T("*&GzUPw9x(Qln6Mf");
	TCHAR szCodeTable1[] = _T("8903jsls912.;asl");
	TCHAR szCodeTable2[] = _T("MbS~?kw`{=|$>12S");
	TCHAR chCode;
	int szCodeTable0Size,szCodeTable1Size,szCodeTable2Size;
	static INT nTableIndex = 0;
	szCodeTable0Size = _tcslen(szCodeTable0);
	szCodeTable1Size = _tcslen(szCodeTable1);
	szCodeTable2Size = _tcslen(szCodeTable2);

	if(nTableIndex++ == 2) 
		nTableIndex = 0;
	switch (nTableIndex)
	{
	case 0:
		chCode = szCodeTable0[nOrder%szCodeTable0Size];
		break;
	case 1:
		chCode = szCodeTable1[nOrder%szCodeTable1Size];
		break;
	case 2:
		chCode = szCodeTable2[nOrder%szCodeTable2Size];
		break;
	}
	return chCode;
}

//////////////////////////////////////////////////////////////////////
// Private: Get Random Character
//////////////////////////////////////////////////////////////////////
TCHAR CUTILregEx::GetRandom()
{
	INT i = rand() & 0x7f;
	return (i < ' ') ? ' ' : (i > 'z') ? 'z' : i;
}

//////////////////////////////////////////////////////////////////////
// Public: return HKEY value of the registry object
//////////////////////////////////////////////////////////////////////
HKEY CUTILregEx::GetHKEY() const
{
	return m_hKey;
}
